#include "probability.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

typedef struct {
	unsigned int num_ports; //number of ports in network switch that packets are generated for
	unsigned int T; //number of time steps that generator should proceed
	unsigned int seed; //the seed for the randomizer;
	double p; //probability for Bernouli procecss
	int (* drawDestPortFunc) (int, int); //pointer to function that used to generate destination port
} GeneratorParameters; //struct used to hold traffic generator parameters 


/* This function reads, verifies and store the command line argiments to generator parameters struct */
int readAndVerifyGeneratorParameters(GeneratorParameters * generator_parameters, int argc, char * argv[]);

/*This function runs the traffic generator for T steps (T parameter stored in struct) */
void runTrafficGenerator(GeneratorParameters * generator_parameters);

/*This function performs a step of traffic generation */
void performTrafficGeneratorStep(GeneratorParameters * generator_parameters, int step_id);

/*This function outputs generated packet's data in required format*/
void outputPacketLine(int arrivale_time, int arrival_port, int destination_port);

/*Helper functions*/

/*Verifies a string argument is a integer number and stores it to <number> parameter */
int isNumeric(char * argument, unsigned int * number);

/*Verifies a string argument is a real number and stores it to <number> parameter */
int isReal(char * argument, double * number);

int isNumeric(char * argument, unsigned int * number) {
	char itoa[10];
	int i=0,len;
	len = strlen(argument);
	do {
		if (!isdigit(argument[i]))
			return 0;
		i++;
	} while (i < len);
	
	sprintf(itoa, "%d", atoi(argument));
	if (strcmp(itoa,argument) != 0) {
		return 0;
	}
	
	if (number != NULL)
		*number = atoi(argument);
	return 1;
}

int isReal(char * argument, double * number) {
	char ftoa[10];
	int i=0,len,decimal=0;
	double x;
	len = strlen(argument);
	do {
		if (!isdigit(argument[i])) {
			if ((i > 0) && (decimal == 0) && (argument[i] == '.'))
				decimal = 1;
			else
				return 0;
		}
		i++;
	} while (i < len);
	

	if (number != NULL)
		*number = atof(argument);
	return 1;
}


int readAndVerifyGeneratorParameters(GeneratorParameters * generator_parameters, int argc, char * argv[]) {

	if (argc > 6 || argc < 5) { /* program needs to get 4 or 5 parameters */
		fprintf(stderr,"Error : input must include 4 or 5 parameters:\ntr_gen N T seed p [-d]\n");
		return -1;
	}

	//first argument should be positive integer number of ports
	if (!isNumeric(argv[1],&(generator_parameters->num_ports))) {
		fprintf(stderr,"Error : invalid switch dimension (N)!\n");
		return -1;
	}

	//second number must be positive integer number of steps
	if (!isNumeric(argv[2],&(generator_parameters->T))) {
		fprintf(stderr,"Error : invalid time steps (T)!\n");
		return -1;
	}

	//third argument must be positive integer seed
	if (!isNumeric(argv[3],&(generator_parameters->seed))) {
		fprintf(stderr,"Error : invalid seed!\n");
		return -1;
	}

	//4th argument must be a real number between 0.0 and 1.0 of probability
	if (!isReal(argv[4],&(generator_parameters->p))) {
		fprintf(stderr,"Error : invalid probability (should be between 0 and 1)!\n");
		return -1;
	}
	if (generator_parameters->p > 1) {
		fprintf(stderr,"Error : invalid probability (should be between 0 and 1)!\n");
		return -1;
	}

	//if there is 6th parameter, it should be "-d"
	if (argc == 6) {
		if (strcmp(argv[5],"-d")) {
			fprintf(stderr,"Error : invalid flag (only -d is supported)!");
			return -1;
		}
		generator_parameters->drawDestPortFunc = NextDiagonalDistributedNumber; //use deagonal distribution in this case
	}
	else //"-d" parameter not entered
		generator_parameters->drawDestPortFunc = NextUniformDistributedNumber; //use uniform distribution in this case

	return 0;
}

void runTrafficGenerator(GeneratorParameters * generator_parameters) {
	
	int i;

	//perform generation step T times
	for (i = 0; i < generator_parameters->T; i++) {
		performTrafficGeneratorStep(generator_parameters, i);
	}
}


void performTrafficGeneratorStep(GeneratorParameters * generator_parameters, int step_id) {
	
	int i;
	int dest;

	//run on all ports
	for (i = 0; i < generator_parameters->num_ports; i++) {
		if (NextBernouliResult() == 0)
			continue; //don't generate packet for this port (Bernouli result if 0)

		//Draw destination port
		dest = generator_parameters->drawDestPortFunc(i, generator_parameters->num_ports);

		//Output the generated packet to stdout
		outputPacketLine(step_id, i, dest);
	}
}

void outputPacketLine(int arrivale_time, int arrival_port, int destination_port) {
	fprintf(stdout, "%d %d %d\n", arrivale_time + 1, arrival_port, destination_port);
}




int main(int argc, char * argv[]) {

	GeneratorParameters generator_parameters; //the parameters for current generator

	if (readAndVerifyGeneratorParameters(&generator_parameters, argc, argv) == -1)
	{
		fprintf(stderr, "Failed to assign generator parameters\n");
		exit(EXIT_FAILURE); //failed to parse command line, terminate
	}

	InitRandomizer(generator_parameters.seed, generator_parameters.p); //init randomizer with seed and Bernoulli probability

	runTrafficGenerator(&generator_parameters); //do the job

	exit(0);
}